package com.netty.rpc.proxy;

import com.netty.rpc.ProxyInhandler.RpcProxyHandler;
import com.netty.rpc.info.InvokeMsg;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * Created by XiongMM on 2017/11/23.
 * 代理类，
 */
public class ProxyClass {

    public static <T> T create(Class<?> clazz){
        MethodProxy methodProxy = new MethodProxy(clazz);
        //得到动态代理对象
        T result = (T) Proxy.newProxyInstance(clazz.getClassLoader(),new Class[]{clazz},methodProxy);
        return result;
    }
}

//实现InvocationHandler的动态代理类
class MethodProxy implements InvocationHandler{

    private Class<?> clazz;

    public MethodProxy(Class<?> clazz) {
        this.clazz = clazz;
    }

    //它只会做一件事，重写invoke方法，对method方法进行重新，从而实现远程调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //如果是普通的动态代理（对接口进行实现的类）
        if(Object.class.equals(method.getDeclaringClass())){
            try{
                return method.invoke(this.clazz,args);
            }catch (Exception ex){
                ex.printStackTrace();
            }
        }
        //走远程调用
        return rpcInvoke(proxy,method,args);
    }

    //具体对远程调用
    private Object rpcInvoke(Object proxy, Method method, Object[] args) {
        //传输协议对象
        InvokeMsg invokeMsg = new InvokeMsg();
        invokeMsg.setClassName(this.clazz.getName());
        invokeMsg.setMethodName(method.getName());
        invokeMsg.setParamType(method.getParameterTypes());
        invokeMsg.setParameters(args);
        //实现ChannelInboundHandlerAdapter，接收返回的信息
        final RpcProxyHandler proxyHandler = new RpcProxyHandler();
        //客户端处理I／O读写事件的NIO线程组
        EventLoopGroup group = new NioEventLoopGroup();
        try{
            //客户端辅助启动类
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    //NioSocketChannel 与服务端不同
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY,true)
                    //handler用于处理网络IO事件
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //解码器，自定义长度解码，大概意思是，我的消息体的最大长度是MAX_VALUE，从第0个字节开始取，前4个字节为length，后面的4是跳过4个字节，所以这里取到的数据为消息体
                            pipeline.addLast("frameDecoder",new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4));
                            //定义类消息头的长度，length为4个字节，4个字节后的数据才是消息体
                            pipeline.addLast("frameEncoder",new LengthFieldPrepender(4));
                            pipeline.addLast("encoder",new ObjectEncoder());
                            pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(this.getClass().getClassLoader())));
                            pipeline.addLast("handler",proxyHandler);
                        }
                    });
            //向服务端发起异步链接操作
            ChannelFuture channelFuture = bootstrap.connect("localhost",8080).sync();
            //发送信息
            channelFuture.channel().writeAndFlush(invokeMsg).sync();
            channelFuture.channel().closeFuture().sync();
        }catch (Exception ex){
            ex.printStackTrace();
        }finally {
            group.shutdownGracefully();
        }
        //返回结果
        return proxyHandler.getResponse();
    }
}
